package web.suzy.oj.manager.admin.problem;

import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import web.suzy.oj.common.exception.StatusFailException;
import web.suzy.oj.common.exception.StatusForbiddenException;
import web.suzy.oj.dao.judge.JudgeEntityService;
import web.suzy.oj.dao.problem.ProblemCaseEntityService;
import web.suzy.oj.dao.problem.ProblemEntityService;
import web.suzy.oj.pojo.dto.ProblemDTO;
import web.suzy.oj.pojo.entity.judge.Judge;
import web.suzy.oj.pojo.entity.problem.Problem;
import web.suzy.oj.pojo.entity.problem.ProblemCase;
import web.suzy.oj.shiro.AccountProfile;
import web.suzy.oj.utils.Constants;
import web.suzy.oj.validator.ProblemValidator;

import javax.annotation.Resource;
import java.io.File;
import java.util.List;

/**
 * YangSuzy 软件工作室
 * 类名: AdminProblemManager
 * 描述: ---- 待定 -----
 * 功能: ---- 待定 -----
 * ==========未完成===========
 * 1.编译分发
 *
 * @author YangSuzy
 * Date: 2022/11/22 14:30
 */
@Component
@RefreshScope
@Slf4j(topic = "suzyoj")
public class AdminProblemManager {

    @Autowired
    private ProblemEntityService problemEntityService;

    @Autowired
    private ProblemCaseEntityService problemCaseEntityService;

    @Resource
    private JudgeEntityService judgeEntityService;

    @Resource
    private ProblemValidator problemValidator;

    /**
     * 方法名: getProblemList
     * 描述: 获取题目列表
     *
     * @param limit       每页显示数目
     * @param currentPage 当前页
     * @param keyword     关键字
     * @param auth        题目权限 1-公开 2-私有
     * @return com.baomidou.mybatisplus.core.metadata.IPage<web.suzy.oj.pojo.entity.problem.Problem>
     * @date 2022/11/22 14:32
     * @auther YangSuzy
     **/
    public IPage<Problem> getProblemList(Integer limit, Integer currentPage, String keyword, Integer auth) {
        //设置默认页码和每页默认显示数目
        if (currentPage == null || currentPage < 1) {
            currentPage = 1;
        }
        if (limit == null || limit < 1) {
            limit = 10;
        }
        IPage<Problem> iPage = new Page<>(currentPage, limit);
        IPage<Problem> problemList;

        QueryWrapper<Problem> queryWrapper = new QueryWrapper<>();
        //根据题目ID降序排序
        queryWrapper.orderByDesc("id");
        //筛选题目权限
        if (auth != null && auth != 0) {
            queryWrapper.eq("auth", auth);
        }
        //筛选关键字
        if (!StringUtils.isEmpty(keyword)) {
            final String key = keyword.trim();
            queryWrapper.and(wrapper -> wrapper.like("title", key).or()
                    .like("author", key).or()
                    .like("problem_id", key));
        }
        problemList = problemEntityService.page(iPage, queryWrapper);
        return problemList;
    }

    /**
     * 方法名: getProblem
     * 描述: 查看题目信息
     *
     * @param pid 题目ID
     * @return web.suzy.oj.pojo.entity.problem.Problem
     * @date 2022/11/22 15:03
     * @auther YangSuzy
     **/
    public Problem getProblem(Long pid) throws StatusForbiddenException, StatusFailException {
        Problem problem = problemEntityService.getById(pid);
        //查询成功
        if (problem != null) {
            //获取当前登录的用户
            AccountProfile userRolesVo = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
            boolean isRoot = SecurityUtils.getSubject().hasRole("root");
            boolean isProblemAdmin = SecurityUtils.getSubject().hasRole("problem_admin");
            //只有超级管理员和题目管理员、题目创建者才能操作
            if (!isRoot && !isProblemAdmin && !userRolesVo.getUsername().equals(problem.getAuthor())) {
                throw new StatusForbiddenException("对不起，你无权限查看题目！");
            }
            return problem;
        }
        throw new StatusFailException("查询失败！");
    }

    /**
     * 方法名: deleteProblem
     * 描述: 删除题目
     *
     * @param pid 题目ID
     * @return void
     * @date 2022/11/22 15:31
     * @auther YangSuzy
     **/
    public void deleteProblem(Long pid) throws StatusFailException {
        //其他以problem_id为外键的表的对应数据会被一起删除
        boolean isOk = problemEntityService.removeById(pid);
        //删除成功后把本地的该题测试用例一并删除
        FileUtil.del(Constants.File.TESTCASE_BASE_FOLDER.getPath() + File.separator + "problem_" + pid);
        AccountProfile userRolesVo = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        log.info("[{}],[{}],pid:[{}],operatorUid:[{}],operatorUsername:[{}]",
                "Admin_Problem", "Delete", pid, userRolesVo.getUid(), userRolesVo.getUsername());
        if (!isOk) {
            throw new StatusFailException("删除失败！");
        }
    }

    /**
     * 方法名: addProblem
     * 描述: 添加题目
     *
     * @param problemDto 题目信息传输对象
     * @return void
     * @date 2022/11/22 15:43
     * @auther YangSuzy
     **/
    public void addProblem(ProblemDTO problemDto) throws StatusFailException {
        //验证器校验题目信息是否符合要求
        problemValidator.validateProblem(problemDto.getProblem());
        //校验 problem_id 唯一性
        QueryWrapper<Problem> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("problem_id", problemDto.getProblem().getProblemId().toUpperCase());
        Problem problem = problemEntityService.getOne(queryWrapper);
        if (problem != null) {
            throw new StatusFailException("该题目的Problem ID已存在，请更换！");
        }
        //添加题目
        boolean isOk = problemEntityService.adminAddProblem(problemDto);
        if (!isOk) {
            throw new StatusFailException("添加失败");
        }
    }

    /**
     * 方法名: updateProblem
     * 描述: 更新题目信息
     *
     * @param problemDto 题目信息传输对象
     * @return void
     * @date 2022/11/22 16:00
     * @auther YangSuzy
     **/
    @Transactional(rollbackFor = Exception.class)
    public void updateProblem(ProblemDTO problemDto) throws StatusForbiddenException, StatusFailException {
        //验证器校验更新后的题目信息
        problemValidator.validateProblemUpdate(problemDto.getProblem());

        //获取当前登录的用户角色信息
        AccountProfile userRolesVo = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        //只有超级管理员和题目管理员、题目创建者才能执行修改操作
        boolean isRoot = SecurityUtils.getSubject().hasRole("root");
        boolean isProblemAdmin = SecurityUtils.getSubject().hasRole("problem_admin");
        if (!isRoot && !isProblemAdmin && !userRolesVo.getUsername().equals(problemDto.getProblem().getAuthor())) {
            throw new StatusForbiddenException("对不起，你无权限修改题目！");
        }

        //校验修改后题目ID的唯一性
        String problemId = problemDto.getProblem().getProblemId().toUpperCase();
        QueryWrapper<Problem> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("problem_id", problemId);
        Problem problem = problemEntityService.getOne(queryWrapper);
        if (problem != null && problem.getId().longValue() != problemDto.getProblem().getId()) {
            throw new StatusFailException("当前的Problem ID 已被使用，请重新更换新的！");
        }

        //记录修改题目的用户
        problemDto.getProblem().setModifiedUser(userRolesVo.getUsername());

        boolean isOk = problemEntityService.adminUpdateProblem(problemDto);
        if (problem == null) {
            //题目信息为空说明修改了problemId，需要同步judge表
            UpdateWrapper<Judge> judgeUpdateWrapper = new UpdateWrapper<>();
            judgeUpdateWrapper.eq("pid", problemDto.getProblem().getId())
                    .set("display_pid", problemId);
            judgeEntityService.update(judgeUpdateWrapper);
        }
        if (!isOk) {
            throw new StatusFailException("修改失败");
        }
    }

    /**
     * 方法名: getProblemCases
     * 描述: 获取题目测试用例
     *
     * @param pid      题目ID
     * @param isUpload 是否使用上传的文件
     * @return java.util.List<web.suzy.oj.pojo.entity.problem.ProblemCase>
     * @date 2022/11/22 18:24
     * @auther YangSuzy
     **/
    public List<ProblemCase> getProblemCases(Long pid, Boolean isUpload) {
        QueryWrapper<ProblemCase> problemCaseQueryWrapper = new QueryWrapper<>();
        //查询可用的测试用例
        problemCaseQueryWrapper.eq("pid", pid).eq("status", 0);
        if (isUpload) {
            //根据输入文件字节数升序排序
            problemCaseQueryWrapper.last("order by length(input) asc,input asc");
        }
        return problemCaseEntityService.list(problemCaseQueryWrapper);
    }

    /**
     * 方法名: changeProblemAuth
     * 描述: 修改题目权限
     *
     * @param problem 题目信息
     * @return void
     * @date 2022/11/22 18:36
     * @auther YangSuzy
     **/
    public void changeProblemAuth(Problem problem) throws StatusFailException, StatusForbiddenException {
        //获取登录用户权限
        boolean root = SecurityUtils.getSubject().hasRole("root");
        boolean problemAdmin = SecurityUtils.getSubject().hasRole("problem_admin");
        //只有题目管理员和超级管理员可以公开题目
        if (!problemAdmin && !root && problem.getAuth() == 1) {
            throw new StatusForbiddenException("修改失败！你无权限公开题目！");
        }

        //获取用户角色信息
        AccountProfile userRolesVo = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        UpdateWrapper<Problem> problemUpdateWrapper = new UpdateWrapper<>();
        //设置题目权限和修改题目的用户信息
        problemUpdateWrapper.eq("id", problem.getId())
                .set("auth", problem.getAuth())
                .set("modified_user", userRolesVo.getUsername());
        boolean isOk = problemEntityService.update(problemUpdateWrapper);
        if (!isOk) {
            throw new StatusFailException("修改失败");
        }
        log.info("[{}],[{}],value:[{}],pid:[{}],operatorUid:[{}],operatorUsername:[{}]",
                "Admin_Problem", "Change_Auth", problem.getAuth(), problem.getId(), userRolesVo.getUid(), userRolesVo.getUsername());
    }
}
